package com.video.myapplication.render;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLES31;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView;
import android.opengl.GLUtils;
import android.util.Log;
import android.widget.ImageView;

import com.video.myapplication.R;
import com.video.myapplication.util.Commit;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.security.acl.LastOwnerException;

import javax.microedition.khronos.egl.EGL;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.security.auth.login.LoginException;

import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
import static android.opengl.EGL15.EGL_OPENGL_ES3_BIT;
import static android.opengl.GLES20.GL_CLAMP_TO_EDGE;
import static android.opengl.GLES20.GL_LINEAR;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_STATIC_DRAW;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_S;
import static android.opengl.GLES20.GL_TEXTURE_WRAP_T;
import static android.opengl.GLES20.glTexParameteri;
import static android.opengl.GLES30.GL_MAP_READ_BIT;
import static android.opengl.GLES31.GL_SHADER_STORAGE_BARRIER_BIT;
import static android.opengl.GLES31.GL_SHADER_STORAGE_BUFFER;

public class ComputerRender implements GLSurfaceView.Renderer {

    private Context context;
    private int buffer[] = new int[4];
    private int mComputeProg;
    private int width = 256;
    private int height = 256;
    private int arraySize = width * height;
    private byte[] yuvBuffer;
    private int[] yBuffer;
    private int[] uBuffer;
    private int[] vBuffer;
    private int[] rgbBuffer;
    private int[] outBuffer;
    private ImageView imageView;



    public ComputerRender(Context context, ImageView imageView){
        this.imageView = imageView;
        InputStream in = context.getResources().openRawResource(R.raw.lena_256x256_yuv420p);
        //获取文件的字节数
        int lenght = 0;
        try {
            lenght =  width * height * 3 / 2;
            //创建byte数组
            yuvBuffer = new byte[lenght];
            //将文件中的数据读到byte数组中
            in.read(yuvBuffer);
            Log.e("fuweciong", String.valueOf(lenght));
        } catch (IOException e) {
            e.printStackTrace();
        }
        rgbBuffer = new int[width * height * 4];
        Log.e("fuweicong", String.valueOf(rgbBuffer.length));
        yBuffer = new int[width * height];
        uBuffer = new int[width * height / 4];
        vBuffer = new int[width * height / 4];
        Log.e("fuweicong", String.valueOf(yuvBuffer[0]));

        for(int i = 0;i < arraySize; i++){
            yBuffer[i] =  yuvBuffer[i] & 0xff;
        }
        Log.e("fuweicong y", String.valueOf(yBuffer[0]));
        for(int i = 0;i < arraySize / 4; i++){
            uBuffer[i] = yuvBuffer[arraySize + i] & 0xff ;
        }
        Log.e("fuweicong u", String.valueOf(uBuffer[10]));

        for(int i = 0;i < arraySize / 4; i++){
            vBuffer[i] = yuvBuffer[arraySize * 5 / 4 + i] & 0xff;
        }
        Log.e("fuweicong v", String.valueOf(uBuffer[10]));

        this.context = context;
    }

    /**
     * RGB ARGB图片颜色数组封装成图片
     * @param data          颜色数组
     * @param height        高度
     * @param width         宽度
     * @return
     */
    public static Bitmap getOriginalBitmap(byte[] data, int height, int width) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        ByteBuffer buffer = ByteBuffer.wrap(data);
        Log.e("fuweicong", String.valueOf(buffer.limit()));
        buffer.position(0);
        bitmap.copyPixelsFromBuffer(buffer);
        return bitmap;
    }



    private void setupSSBufferObject(int index, int[] data, int count)
    {
        GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer[index]);
        if (data == null){
            GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER, 0, null, GL_STATIC_DRAW);
        } else {
            GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER, count * Integer.SIZE / 8, IntBuffer.wrap(data), GL_STATIC_DRAW);
        }
        GLES31.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, buffer[index]);
    }


    private void initBuffer(){
        GLES31.glGenBuffers(4, buffer, 0);

        setupSSBufferObject(0, yBuffer, arraySize);
        setupSSBufferObject(1, uBuffer, arraySize / 4);
        setupSSBufferObject(2, vBuffer, arraySize / 4);
        setupSSBufferObject(3,rgbBuffer, arraySize * 4);
    }


    private void checkGlError(String op) {
        int error;
        while ((error = GLES31.glGetError()) != GLES31.GL_NO_ERROR) {
            Log.e("SurfaceTest", op + ": glError " + GLUtils.getEGLErrorString(error));
        }
    }

    private void initGLSL() {
        String source = Commit.readShaderFromRaw(context, R.raw.fragment);

        int computerShaderHandle = GLES31.glCreateShader(GLES31.GL_COMPUTE_SHADER);
        GLES31.glShaderSource(computerShaderHandle,source);
        GLES31.glCompileShader(computerShaderHandle);
        checkGlError("GL_COMPUTE_SHADER compile");


        mComputeProg = GLES31.glCreateProgram();
        GLES31.glAttachShader(mComputeProg, computerShaderHandle);
        GLES31.glLinkProgram(mComputeProg);
        checkGlError("Shader program compile");


        int[] status = new int[1];
        GLES31.glGetProgramiv(mComputeProg, GLES31.GL_LINK_STATUS, status, 0);
        if (status[0] != GLES31.GL_TRUE) {
            String error = GLES31.glGetProgramInfoLog(mComputeProg);
            Log.e("SurfaceTest", "Error while linking program:" + error);
        }
    }


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        Log.e("fuweicong", "created");
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        Log.e("fuweicong", "onSurfaceChanged");
        gl.glViewport(0, 0, width, height);
    }

    /**
     * 保存文件
     * @param bm
     * @param fileName
     * @throws IOException
     */
    public void saveFile(Bitmap bm, String fileName) throws IOException {
        String path = context.getFilesDir() +"/revoeye/";
//        String path = "/goxd2/";
        Log.e("path", path);
        File dirFile = new File(path);
        if(!dirFile.exists()){
            dirFile.mkdir();
        }
        File myCaptureFile = new File(path + fileName);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile));
        bm.compress(Bitmap.CompressFormat.JPEG, 80, bos);
        bos.flush();
        bos.close();
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        Log.e("fuweicong", "onDrawFrame");

        initGLSL();
        checkGlError("initGLSL");

        initBuffer();
        checkGlError("initBuffer");

        GLES31.glUseProgram(mComputeProg);
        checkGlError("glUseProgram");

        GLES31.glDispatchCompute(width,height,1);   // arraySize/local_size_x
        checkGlError("glDispatchCompute");

        GLES31.glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

        GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer[3]);

        checkGlError("glMemoryBarrier");


        ByteBuffer buffer = (ByteBuffer) GLES31.glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, arraySize * 4 * Integer.SIZE / 8, GL_MAP_READ_BIT);
        checkGlError("glMapBufferRange");
        if (buffer != null){
            byte[] data = new byte[buffer.limit()];
            Log.e("fuweicong", String.valueOf(data.length));
            outBuffer = new int[data.length / (Integer.SIZE / 8)];
            buffer.get(data);
            for(int i = 0;i < outBuffer.length;i++){
                byte[] b = {data[i * 4 + 3], data[i * 4 + 2], data[i * 4  + 1], data[i * 4 + 0]};
                DataInputStream dis=new DataInputStream(new ByteArrayInputStream(b));
                int f= -1;
                try {
                    f = dis.readInt();
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                outBuffer[i] = f;
            }
            Log.e("fuweicong", String.valueOf(outBuffer[2]));
            Log.e("fuweicong", String.valueOf(outBuffer[1]));
            Log.e("fuweicong", String.valueOf(outBuffer[0]));

            byte[] newData = new byte[outBuffer.length];
            for(int i = 0;i < newData.length;i++){
                newData[i] = (byte) (outBuffer[i]);
            }
            Log.e("fuweicong", String.valueOf(newData[2]));

            try {
                saveFile(getOriginalBitmap(newData,height,width), "yuv.png");
            } catch (IOException e) {
                e.printStackTrace();
            }
//            imageView.setImageBitmap(getOriginalBitmap(newData,height,width));
        } else {
            Log.e("fuweicong", "没有数据");
        }
    }
}
